local CObjectBase = Import("logic/object/base_object").CObjectBase
local EventEnum = Import("logic/base/event_enum").Enum
local Tool = Import("logic/common/tool").CTool

CUIBaseLogic = class(CObjectBase)
CUIBaseLogic.__name = "CUIBaseLogic"

--构造函数，初始化View，绑定事件等
CUIBaseLogic.Init = function(self, id)
	CObjectBase.Init(self)

	self._id = id
	self._parentLogicIns = nil
	self._closeDlg = nil

	self._effectDlg = nil
	self._childLogicInsList = {ref = {}, mgr = {}}
	self.__uiShowHidelistener = {}

	self:BindUIEvent()
	self:RegistOnOff()
	self:CheckShowHide()

	self._isShow = false
	self._requestDataWeakRefObj = {}
	self._isDestroyGO = true

	self._closeCallBack = nil

	self._dontDestroyOnLoad = false

	self._pnlCom = self._view._root:GetComponent("UIPanelPopup")
	if not self._pnlCom then
		local imgBgTran = self._view._root.transform:Find("ImgBg")

		if imgBgTran then
			self._pnlCom = imgBgTran:GetComponent("UIPanelPopup")
		else
			imgBgTran = self._view._root.transform:Find("Nego_ImgBg")

			if imgBgTran then
				self._pnlCom = imgBgTran:GetComponent("UIPanelPopup")
			else
				local childCount = self._view._root.transform.childCount

				for i=1, childCount do
					imgBgTran = self._view._root.transform:GetChild(i-1)
					self._pnlCom = imgBgTran:GetComponent("UIPanelPopup")
					if self._pnlCom then
						break
					end
				end

			end
		end
	end

	if self._pnlCom then
		self._pnlCom.NeedGfxAlpha = true
	end
end

CUIBaseLogic.GetObj = function(self)
	if not self._view then return end

	return self._view._root
end

CUIBaseLogic.IsRootGo = function(self, go)
	if self._view and self._view._root == go then return true end

	return false
end

CUIBaseLogic.SetParentTran = function(self, parentTran, worldPositionStays)
	worldPositionStays = worldPositionStays and true or false 
	self._view._root.transform:SetParent(parentTran, worldPositionStays)

	return self
end

-- 仅仅添加一个引用parent logic ins，生命周期由其他模块管理
CUIBaseLogic.SetParentLogic = function(self, parentLogicIns)
	self:SetParentLogicIns(parentLogicIns)
end

-- 子欲养而亲不在, 取的时候要注意 parent logic ins 有没有被其他模块Destroy了
CUIBaseLogic.GetParentLogic = function(self)
	return self:GetParentLogicIns()
end

CUIBaseLogic.SetParentLogicIns = function(self, parentLogicIns)
	self._parentLogicIns = parentLogicIns
end

CUIBaseLogic.GetParentLogicIns = function(self)
	return self._parentLogicIns
end

-- 创建mgr child, 确立父子关系
CUIBaseLogic.CreateChildLogicIns = function(self, id, uiLogic, parentTran)
	local childLogicIns = self:GetMgrChildLogicIns(id)
	if not childLogicIns then
		childLogicIns = gGame:GetUIMgr():CreateUILogic(uiLogic, parentTran, id)
		childLogicIns:SetParentLogicIns(self)
		self:AddMgrChildLogicIns(childLogicIns)
	end

	return childLogicIns
end

-- 添加引用ins，destroy的时候只置nil, 不会触发ins destroy
CUIBaseLogic.AddRefChildLogicIns = function(self, childLogicIns)
	local childId = childLogicIns:GetId()

	if self._childLogicInsList["ref"][childId] then
		errot_stack("已存在 child logic id:", childId)
		return
	end

	self._childLogicInsList["ref"][childId] = childLogicIns
end

-- 添加管理ins，destroy的时候会触发ins destroy，同时置nil
CUIBaseLogic.AddMgrChildLogicIns = function(self, childLogicIns)
	local childId = childLogicIns:GetId()

	if self._childLogicInsList["mgr"][childId] then
		errot_stack("已存在 child logic id:", childId)
		return
	end

	self._childLogicInsList["mgr"][childId] = childLogicIns
end

--获取引用 child ins
CUIBaseLogic.GetRefChildLogicIns = function(self, id)
	return self._childLogicInsList["ref"][id]
end

CUIBaseLogic.GetChildLogicIns = function(self, id)
	return self:GetMgrChildLogicIns(id)
end

--获取管理 child ins
CUIBaseLogic.GetMgrChildLogicIns = function(self, id)
	return self._childLogicInsList["mgr"][id]
end

CUIBaseLogic.DestroyChildLogicIns = function(self, id)
	if self._childLogicInsList["mgr"][id] then
		self._childLogicInsList["mgr"][id]:Destroy()
		self._childLogicInsList["mgr"][id] = nil
	end
end

CUIBaseLogic.ReleaseChildLogicIns = function(self, id)
	if self._childLogicInsList["ref"][id] then
		self._childLogicInsList["ref"][id] = nil
	end
end

CUIBaseLogic.ClearChildLogicInsList = function(self)
	self._childLogicInsList["ref"] = {}

	for _, logicIns in pairs(self._childLogicInsList["mgr"]) do
		logicIns:Destroy()
	end

	self._childLogicInsList["mgr"] = {}
end

CUIBaseLogic.SetLocalPos = function(self, localPos) 
	self._view._root.transform.localPosition = localPos
end

CUIBaseLogic.GetLocalPos = function(self)
	return self._view._root.transform.localPosition
end 

CUIBaseLogic.GetAnchoredPosition = function(self)
	local rtf = self._view._root:GetComponent("RectTransform")
	if rtf then
		return rtf.anchoredPosition
	end
end

CUIBaseLogic.SetAnchoredPosition = function(self, anchoredPos)
	local rtf = self._view._root:GetComponent("RectTransform")
	if rtf then
		rtf.anchoredPosition = anchoredPos
	end
end

CUIBaseLogic.GetAnchoredPosition3D = function(self)
	local rtf = self._view._root:GetComponent("RectTransform")
	if rtf then
		return rtf.anchoredPosition3D
	end
end

CUIBaseLogic.SetAnchoredPosition3D = function(self, anchoredPos3D)
	local rtf = self._view._root:GetComponent("RectTransform")
	if rtf then
		rtf.anchoredPosition3D = anchoredPos3D
	end
end

CUIBaseLogic.SetLocalScale = function(self, localScale) 
	self._view._root.transform.localScale = localScale
end 

CUIBaseLogic.SetLocalRotation = function(self, localEulerAngles)
	self._view._root.transform.localEulerAngles = localEulerAngles
end 

CUIBaseLogic.GetRectTransform = function(self)
	local rectTransform = self._view._root:GetComponent("RectTransform")
	return rectTransform
end 

CUIBaseLogic.GetHeight = function(self)
	local rectTransform = self:GetRectTransform()
	if rectTransform then 
		return rectTransform.rect.height
	end 
end 

CUIBaseLogic.SetBackground = function(self, toDestroy, isDelay)
	toDestroy = toDestroy == nil and true or toDestroy
	if isDelay == nil then
		isDelay = true
	end
	local CUICloseDlgLogic = Import("logic/ui/close_dlg/ui_close_dlg_logic").CUICloseDlgLogic
	self._closeDlg = self:CreateLogic(CUICloseDlgLogic, self._view._root.transform)
	self._closeDlg:SetBackground(self._view._root.transform, 0, true)
	self._closeDlg:SetCloseDlg(function() self:OnClickBackground(toDestroy, isDelay) end, nil, false) 
end

CUIBaseLogic.CreatePageEffect = function(self)
	if not self._view._Nego_Effect then return end
	local CUIPageEffectLogic = Import("logic/ui/page_effect/ui_page_effect_logic").CUIPageEffectLogic
	self._effectDlg = CUIPageEffectLogic:New("CUIPageEffectLogic")
	self._effectDlg:SetParentTran(self._view._Nego_Effect)
	self._effectDlg:ShowSwitch(true)
end

CUIBaseLogic.GetPageEffect = function(self)
	return self._effectDlg
end

CUIBaseLogic.OnClickBackground = function(self, toDestroy, isDelay)
	self:CloseWnd(toDestroy, isDelay)
end

CUIBaseLogic.CloseWnd = function(self, toDestroy, isDelay)
	if toDestroy then
		if isDelay then
			if self._pnlCom then
				self._pnlCom.isShow = false
			end
		end
		gGame:GetUIMgr():DestroyWindow(self:GetId(), isDelay)
	else
		self:ShowSwitch(false)
	end 
end

CUIBaseLogic.GetId = function(self)
	return self._id
end

CUIBaseLogic.GetPreFabPath = function(self)
	return self._prefabPath or "null"
end

-- 设置显示
CUIBaseLogic.SetActive = function(self, isActive)
	if self._view then 
		self._view._root:SetActive(isActive)
	end 
end

-- create的回调方法
CUIBaseLogic.OnCreate = function(self)
end

CUIBaseLogic.SetSiblingIndex = function(self, layer)
	self._view._root.transform:SetSiblingIndex(layer)
end

CUIBaseLogic.GetSiblingIndex = function(self)
	return self._view._root.transform:GetSiblingIndex()
end

CUIBaseLogic.SetAsLastSibling = function(self)
	self._view._root.transform:SetAsLastSibling()
end

CUIBaseLogic.SetAsFirstSibling = function(self)
	self._view._root.transform:SetAsFirstSibling()
end

CUIBaseLogic.SetDestroyGO = function(self, value)
	self._isDestroyGO = value
end

CUIBaseLogic.IsDestroyGO = function(self)
	return self._isDestroyGO
end

CUIBaseLogic.SetDontDestroyOnLoad = function(self, dontDestroyOnLoad)
	self._dontDestroyOnLoad = dontDestroyOnLoad
	self:SetOnOffDontDestroy(dontDestroyOnLoad)
end

CUIBaseLogic.SetOnOffDontDestroy = function(self, dontDestroyOnLoad)
	local onOffRegistor = self._view._root:GetComponent("OnOffRegistor")
	if not onOffRegistor then return end

	local viewId = onOffRegistor.viewId

	gGame:GetGuideMgr():SetDontDestroyViewId(viewId, dontDestroyOnLoad)

	local widgets = onOffRegistor:GetWidgets()
	for i = 0, widgets.Length - 1 do
		if widgets[i].widget and (not widgets[i].isPath) then
			--gGame:GetGuideMgr():RegistViewWidget(self._view._viewId, widgets[i].widgetId, widgets[i].widget.transform)
			gGame:GetGuideMgr():SetDontDestroyWidgetId(widgets[i].widgetId, dontDestroyOnLoad)
		end

		if widgets[i].isPath then 
			--gGame:GetGuideMgr():RegistDynamicViewWidget(self._view._viewId, self._view._root, widgets[i].widgetId, widgets[i].widgetPath, widgets[i].childIndex)
			gGame:GetGuideMgr():SetDontDestroyWidgetId(widgets[i].widgetId, dontDestroyOnLoad)     
		end 
	end
end

CUIBaseLogic.IsDontDestroyOnLoad = function(self)
	return self._dontDestroyOnLoad
end

-- destory的回调方法
CUIBaseLogic.OnDestroy = function(self)
	if self._guideTimerId then 
		gGame:GetTimeManager():RemoveTimer(self._guideTimerId)
		self._guideTimerId = nil 
	end 

	if self._view and self._view._root then
		if tolua.isnull(self._view._root) then
			warn_stack("self._view._root的GO提前被释放, UI释放顺序问题, 可能会导致无法GC, PreFabPath:", self:GetPreFabPath())
		else
			UIEventListener.Clear(self._view._root)
		end
	end

	self:UnRegistShowHide()
	self._isShow = false
	self:DestroyInputNumLimitTimer()
	self:CheckFireCloseCallBack()
	CObjectBase.OnDestroy(self)
end

CUIBaseLogic.IsShow = function(self)
	return self._isShow
end

CUIBaseLogic.ShowSwitch = function(self, isShow, isDelay)
	if not isDelay then
		self:DoShowSwitch(isShow)
	else
		gGame:GetTimeManager():SetTimeOut(0.0, function()
			self:DoShowSwitch(isShow)
		end)
	end
end

CUIBaseLogic.DoShowSwitch = function(self, isShow)
	if isShow and self._isShow ~= isShow then
		self._isShow = isShow
		self:OnShow()
		-- 下一帧才触发指引，因为下一帧GameObject.activeInHierarchy才为true
		-- 对于动态生成的child，则需要更多时间GameObject.activeInHierarchy才为true
		self._guideTimerId = gGame:GetTimeManager():SetTimeOut(0.08, function()
			if (not self._view) or (not self._view._viewId) then return end
			gGame:GetGuideMgr():OnShowView(self._view._viewId)
		end)
	elseif not isShow and self._isShow ~= isShow then
		self._isShow = isShow
		self:OnHide()
	end
end

-- 如果显示有动画的话，就重写这个方法
CUIBaseLogic.OnShow = function(self)
	self:SetActive(true)
	if self._pnlCom then
		self._pnlCom.isShow = true
	end
	self:CheckShowHide()
end

-- 如果显示有动画的话，就重写这个方法
CUIBaseLogic.OnHide = function(self)
	if self._pnlCom then
		self._pnlCom.isShow = false
		gGame:GetTimeManager():SetTimeOut(0.2, function()
			if self._isDestroy then return end
			if self._view._root then
				self:SetActive(false)
				self._isShow = false
			end
		end)
	else
		self:SetActive(false)
	end
end

CUIBaseLogic.Destroy = function(self)
	self._requestDataWeakRefObj = nil
	self._pnlCom = nil

	if self._closeDlg then
		gGame:GetUIMgr():DestroyWindow(self._closeDlg:GetId())
		self._closeDlg = nil
	end

	self._parentLogicIns = nil
	self:ClearChildLogicInsList()
	self:OnDestroy()

	self._id = nil

	if self._view then
		if self._view._viewId then
			gGame:GetGuideMgr():OnDestroyView(self._view._viewId)
			gGame:GetGuideMgr():UnRegistViewWidget(self._view._viewId)
			self._view._viewId = nil
		end
		
		if self._isDestroyGO then
			GameObject.Destroy(self._view._root)
			self._view._root = nil
		end
		
		self._view = nil
	end

	if self._effectDlg then
		self._effectDlg:Destroy()
		self._effectDlg = nil
	end
end

CUIBaseLogic.BindUIEvent = function(self)
end

CUIBaseLogic.SendUICommonStatusEvent = function(self, status, id)
	id = id or self._id
	EventTrigger(EventEnum.UI_COMMON_STATUS, id, status)
end

-- 检查分页是否能打开
CUIBaseLogic.OnOpenPanelCheck = function(self, go)
	return gGame:GetGuideMgr():OnOpenPanelCheck(self._view._viewId, go)
end 

CUIBaseLogic.RegistOnOff = function(self)
	local onOffRegistor = self._view._root:GetComponent("OnOffRegistor")
	if not onOffRegistor then return end

	-- 当等级满足条件时显示等级相关UI
	-- 只在存在OnOffRegistor组件的情况下才去监听
	if not self._registShowHide then 
		self:RegistShowHide() 
		self._registShowHide = true
	end 

	self._view._viewId = onOffRegistor.viewId

	local widgets = onOffRegistor:GetWidgets()
	local guideMgr = gGame:GetGuideMgr()

	for i = 0, widgets.Length - 1 do
		local widget = widgets[i]
		if widget.widget and (not widget.isPath) then
			guideMgr:RegistViewWidget(self._view._viewId, widget.widgetId, widget.widget.transform)
		end

		if widget.isPath then 
			guideMgr:RegistDynamicViewWidget(self._view._viewId, self._view._root, widget.widgetId, widget.widgetPath, widget.childIndex)
		end 

		if widget.isDrag then 
			guideMgr:RegistDragDestViewWidget(self._view._viewId, widget.widgetId, widget.dragDestWidget.transform)
		end 
	end
end

-- 注册ui显示隐藏的条件
CUIBaseLogic.RegistShowHide = function(self)
	local keyGrade = "iGrade"
	local heroData = gGame:GetDataMgr():GetHeroData() 
	local eventName, func = heroData:RegModifyEvent(keyGrade, function(_, keyGrade, curValue, oldValue)
		self:CheckShowHide(true)
	end)
	self.__uiShowHidelistener[eventName] = func
end

-- 控制按钮等的显示隐藏
CUIBaseLogic.CheckShowHide = function(self, openNewSys)
	gGame:GetGuideMgr():CheckShowHide(self._view._viewId, openNewSys)
end 

CUIBaseLogic.IsHideWidget = function(self, go)
	return gGame:GetGuideMgr():IsHideWidget(self._view._viewId, go)
end

CUIBaseLogic.GetViewId = function(self)
	return self._view._viewId
end

CUIBaseLogic.AskRpc = function(self, protocol, ...)
	gGame:GetProtocolMgr():askRpc(protocol, ...)
end

CUIBaseLogic.UnRegistShowHide = function(self)
	local heroData = gGame:GetDataMgr():GetHeroData()

	for eventName, func in pairs(self.__uiShowHidelistener) do
		heroData:UnRegModifyEvent(eventName, func)
	end 
end

CUIBaseLogic.CreateLogic = function(self, uiLogicClass, parentTran, id)
	return gGame:GetUIMgr():CreateUILogic(uiLogicClass, parentTran, id)
end

-- 需要显示【输入字数/限制字数】的界面可以调用这个接口
-- 传入输入内容InputField组件，以及显示提示的Text组件
CUIBaseLogic.ShowInputNumLimit = function(self, inputField, textLimitTips)
	self._inputContentInputField = inputField
	self._inputNumTipsText = textLimitTips

	if self._isUpdatingInputNumTips then
		UpdateBeat:Remove(self.UpdateInputNumLimiteTips, self)
	end

	UpdateBeat:Add(self.UpdateInputNumLimiteTips, self)
	self._isUpdatingInputNumTips = true
end

-- UpdateBeat调用对InputField的内容监听
CUIBaseLogic.UpdateInputNumLimiteTips = function(self)
	if not self._inputContentInputField then return end

	local currentInputContent = self._inputContentInputField.text
	if currentInputContent ~= self._previousInputContent then

		self._previousInputContent = currentInputContent
		local totalInputNum = self._inputContentInputField.characterLimit
		local currentInputNum = utf8.len(currentInputContent)
		self._inputNumTipsText.text = currentInputNum .. "/" .. totalInputNum

		if currentInputNum == totalInputNum then
			self:OnEnterCharacterLimitNum()
		end
	end
end

CUIBaseLogic.OnEnterCharacterLimitNum = function(self)
end

-- 界面销毁时，如果有调用过显示输入字数限制，则移除对应定时器
CUIBaseLogic.DestroyInputNumLimitTimer = function(self)
	if self._isUpdatingInputNumTips then
		UpdateBeat:Remove(self.UpdateInputNumLimiteTips, self)
		self._inputContentInputField = nil
		self._inputNumTipsText = nil
	end
end

-- 将UI适配iPhoneX，全屏UI才有可能有遮盖问题，因为iPhoneX的刘海是盖在画布上，需要把主要显示内容放在安全区域
-- 实现方法：将UI的锚框大小缩小到安全区域，两遍的UI锚在两边上，同时背景图进行放大来填充黑底
CUIBaseLogic.AdaptUIForIphoneX = function(self, goBgList)
	if not self:CheckIsIphoneX() then return end

	local rtfRoot = self._view._root:GetComponent("RectTransform")
	if not rtfRoot then return end

	-- 调整锚框，留出空白
	rtfRoot.anchorMin = Vector2.New(0.04, 0)
	rtfRoot.anchorMax = Vector2.New(0.96, 1)

	-- 调整背景对象大小，填补黑底
	if not goBgList then return end
	for _, goBg in pairs(goBgList) do
		goBg.transform.localScale = Vector3.New(1.15, 1.15, 1)
	end

end

local IPHONEX_SCREEN_WIDTH = 2436
local IPHONEX_SCREEN_HEIGHT = 1125

-- 检测是不是iPhoneX
CUIBaseLogic.CheckIsIphoneX = function(self)
	-- 根据平台和分辨率，有更好的办法可以替换
	if Application.platform ~= RuntimePlatform.IPhonePlayer then return false end
	if Screen.width ~= IPHONEX_SCREEN_WIDTH then return false end
	if Screen.height ~= IPHONEX_SCREEN_HEIGHT then return false end
	return true
end

-- 关闭界面时的回调，可用于生成界面时的外部设置
CUIBaseLogic.SetCloseCallBack = function(self, callBack)
	if type(callBack) ~= "function" then return end
	self._closeCallBack = callBack
end

CUIBaseLogic.CheckFireCloseCallBack = function(self)
	if not self._closeCallBack then return end
	self._closeCallBack()
	self._closeCallBack = nil
end

CUIBaseLogic.ClearCloseCallBack = function(self)
	self._closeCallBack = nil
end

CUIBaseLogic.GetAsyncAssetPath = function(self)
	return ""
end

local UI_ADAPT_ERROR_RANGE = 0.05 -- 屏幕分辨率误差值
-- 全屏UI适配不同分辨率
CUIBaseLogic.BeginAutoConfigBlackBound = function(self, isTop, isHideBlack)
	local ratio_4_3 = 4 / 3
	local ratio_2_1 = 2 / 1
	local ratio = Screen.width / Screen.height

	if not tolua.isnull(self._blackBoundGo) then
		GameObject.Destroy(self._blackBoundGo)
		self._blackBoundGo = nil
	end

	-- 4:3屏幕
	if math.abs(ratio - ratio_4_3) <= UI_ADAPT_ERROR_RANGE then
		self._blackBoundGo = Tool.Instantiate("Assets/res/Prefabs/Common/UIBlackBound_4_3.prefab")
	end

	-- 2:1屏幕
	-- 市面上2:1手机比例比较多，有2.055, 2.111, 2.166，所以判断条件这样写，黑边UI按最长的2.166来出
	if (math.abs(ratio - ratio_2_1) <= UI_ADAPT_ERROR_RANGE) or (ratio > ratio_2_1) then
		--[[
			全屏带背景的UI，美术的规范出图应该要出2:1的图片（1440:720），画布的参考分辨率应设置为1280:720
			在16:9和4:3的情况下，选择为宽度匹配，即16:9时显示1440:720中的1280:720部分，4:3的时候上下加黑边
			在2:1的时候，选择为高度匹配，这个时候1440:720的背景就刚好填充满屏幕
			但是目前UI并没有替换完毕，所以需要isHideBlack这个字段，部分界面在2:1分辨率的时候需要填充黑边
		--]]

		if isHideBlack then return end
		self._blackBoundGo = Tool.Instantiate("Assets/res/Prefabs/Common/UIBlackBound_2_1.prefab")
	end

	if not self._blackBoundGo then return end

	local topCanvasTr = gGame:GetUIMgr():GetTopCanvasTr()
	local mainCanvasTr = gGame:GetUIMgr():GetMainCanvasTr()

	-- 黑边挂在最高
	if isTop then
		self._blackBoundGo.transform:SetParent(topCanvasTr, false)
		self._blackBoundGo.transform:SetAsLastSibling()

	-- 黑边挂在最底
	else
		self._blackBoundGo.transform:SetParent(mainCanvasTr, false)
		self._blackBoundGo.transform:SetAsFirstSibling()
	end
end

-- 取消适配不同分辨率
CUIBaseLogic.EndAutoConfigBlackBound = function(self)
	if not tolua.isnull(self._blackBoundGo) then
		GameObject.Destroy(self._blackBoundGo)
		self._blackBoundGo = nil
	end
end

-- 设置是否显示：角色属性飘字，后端发过来的浮动tell me，落袋效果
CUIBaseLogic.SwitchIsShowFloatMsg = function(self, isShow)
	gGame:GetDataMgr():GetHeroData():SwitchArrtTips(isShow)
	gGame:GetDataMgr():GetMsgData():SwitchMsg(not isShow)
end